/**
 * Generali Solutions d'assurances - Tous droits réservés &copy; 2007 - 2010
 */
package fr.generali.accueilclient.commun.tosca;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.xml.datatype.XMLGregorianCalendar;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.Transformer;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.junit.Assert;

/**
 * @author Holmes Kelly
 */
public abstract class AbstractBcToscaUnitilsJUnit4TestCaseHelper extends AbstractBcToscaUnitilsJUnit4TestCase {

    /**
     * prefix get
     */
    private static final String STRING_GET = "get";

    /**
     * prefix set
     */
    private static final String STRING_SET = "set";

    /**
     * @param objectExpect expect object
     * @param objectTest test object
     * @throws Exception .
     */
    protected void comparePropertyFields(Object objectExpect, Object objectTest) throws Exception {

        final Field[] fieldsExpect = objectExpect.getClass().getDeclaredFields();

        List<Boolean> conditions = new ArrayList<Boolean>();

        for (final Field field : fieldsExpect) {

            conditions.clear();

            Collections.addAll(conditions, field.getType().getName().equals(String.class.getName()), field.getType()
                            .getName().equals(BigInteger.class.getName()), field.getType().getName().equals(
                            BigDecimal.class.getName()), field.getType().getName().equals(
                            XMLGregorianCalendar.class.getName()), field.getType().getName().equals(
                            Date.class.getName()));

            if (isFieldHasGetterMethod(objectExpect.getClass(), field)
                            && BooleanUtils.xor(conditions.toArray(new Boolean[conditions.size()]))) {

                Assert.assertEquals(BeanUtils.getNestedProperty(objectExpect, field.getName()), BeanUtils
                                .getNestedProperty(objectTest, field.getName()));

            }

        }
    }

    /**
     * @param objectExpect expect object
     * @param objectTest test object
     * @throws Exception exception
     */
    protected void compareBeans(Object objectExpect, Object objectTest) throws Exception {

        if (objectExpect instanceof Collection< ? > && objectTest instanceof Collection< ? >) {

            Iterator< ? > iteratorExpect = ((Collection< ? > ) objectExpect).iterator();

            Iterator< ? > iteratorTest = ((Collection< ? > ) objectTest).iterator();

            while (iteratorExpect.hasNext() && iteratorTest.hasNext()) {

                Object itemExpect = iteratorExpect.next();

                Object itemTest = iteratorTest.next();

                comparePropertyFields(itemExpect, itemTest);

            }
        } else {

            comparePropertyFields(objectExpect, objectTest);
        }

    }

    /**
     * @param classBean class
     * @return transformer
     */
    protected Transformer getTransformerBean(Class< ? > classBean) {

        final Class< ? > classBeanTrans = classBean;

        Transformer transformer = new Transformer() {

            public Object transform(Object input) {

                return classBeanTrans.cast(input);
            }
        };

        return transformer;
    }

    /**
     * @param <T> type parametre
     * @param beanId dans application context
     * @param classBean class du bean
     * @return list
     */
    protected <T> List<T> createInstanceListObject(String beanId, Class<T> classBean) {

        List<T> list = new ArrayList<T>();

        Object bean = applicationContext.getBean(beanId);

        if (bean != null && bean instanceof List< ? >) {

            List< ? > listBean = (List< ? > ) bean;

            CollectionUtils.collect(listBean, getTransformerBean(classBean), list);
        }

        return list;
    }

    /**
     * @param <T> type parametre
     * @param beanId dans application context
     * @param classBean class du bean
     * @return bean
     */
    protected <T> T createInstanceObject(String beanId, Class<T> classBean) {

        T target = null;

        Object bean = applicationContext.getBean(beanId);

        if (bean != null && bean.getClass().getName().equals(classBean.getName())) {

            target = classBean.cast(bean);
        }

        return target;

    }

    /**
     * @param clazz la classe
     * @param prefix le prefix
     * @return map accesseur
     */
    private Map<String, Method> getAccessor(Class< ? > clazz, String prefix) {

        Method[] methods = clazz.getDeclaredMethods();
        Map<String, Method> methodMap = new HashMap<String, Method>();

        for (Method method : methods) {
            String methodName = method.getName();
            // On ajoute la methode si elle ne contient pas de parametre
            // et si le nom de la methode commence par "get"
            boolean isMethodePrefixGet =
                            STRING_GET.equals(prefix) && Arrays.asList(method.getParameterTypes()).size() == 0;

            boolean isMethodePrefixSet =
                            STRING_SET.equals(prefix) && Arrays.asList(method.getParameterTypes()).size() == 1;

            if ((isMethodePrefixGet || isMethodePrefixSet) && prefix.equals(StringUtils.left(methodName, 3))) {
                methodMap.put(methodName, method);
            }
        }
        return methodMap;
    }

    /**
     * @param clazz la classe
     * @param field le champ
     * @return boolean check
     */
    private boolean isFieldHasGetterMethod(Class< ? > clazz, Field field) {

        boolean isFieldHasGetterMethod = false;

        Map<String, Method> mapAccessor = getAccessor(clazz, STRING_GET);

        isFieldHasGetterMethod = CollectionUtils.exists(mapAccessor.keySet(), getPredicate(field.getName()));

        return isFieldHasGetterMethod;
    }

    /**
     * @param fieldName fieldName
     * @return predicat
     */
    private Predicate getPredicate(String fieldName) {

        final String fieldNameTest = fieldName;

        Predicate predicate = new Predicate() {

            public boolean evaluate(Object object) {

                String methodName = String.class.cast(object);

                return methodName.toLowerCase().contains(fieldNameTest.toLowerCase());
            }
        };

        return predicate;
    }
}
